#ifndef AC_COMMON_POSIB_ERR_HPP_
#define AC_COMMON_POSIB_ERR_HPP_

#include "string.hpp"
#include "error.hpp"
#include "errors.hpp"

enum WhichErr {PrimErr, SecErr};

extern "C" const ErrorInfo* const perror_bad_file_format;

template <typename Ret> class PosibErr;

class PosibErrBase
{
private:
    struct ErrPtr
    {
        const Error* err;
        int     refcount;
        ErrPtr(const Error* e)
            : err(e), refcount(1)
        {
        }
    };
    ErrPtr* err_;

protected:
    void posib_handle_err() const
    {
    }

    void copy(const PosibErrBase& other)
    {
        err_ = other.err_;
        if (err_)
        {
            ++ err_->refcount;
        }
    }

    void destroy()
    {
        if (err_ == 0)
            return ;
        -- err_->refcount;
        if (err_->refcount == 0)
        {
            del();
        }
    }

public:
    PosibErrBase()
        : err_(0)
    {
    }

    PosibErrBase(const PosibErrBase& other)
    {
        copy(other);
    }

    PosibErrBase& operator = (const PosibErrBase& other)
    {
        destroy();
        copy(other);
        return *this;
    }

    ~PosibErrBase()
    {
        destroy();
    }

    Error* release_err()
    {
        if (err_ == 0)
            return 0;
        else
            return release();
    }

    void ignore_err()
    {
    }

    const Error* get_err() const
    {
        if (err_ == 0)
            return 0;
        else
            return err_->err;
    }

    const Error* prvw_err() const
    {
        if (err_ == 0)
            return 0;
        else
            return err_->err;
    }

    bool has_err() const
    {
        return err_ != 0;
    }

    bool has_err(const ErrorInfo* e) const
    {
        if (err_ == 0)
            return false;
        else
        {
            if (err_->err->is_a(e))
                return true;
            else
                return false;
        }
    }

    PosibErrBase& prim_err(const ErrorInfo* inf, ParmString p1 = 0,
        ParmString p2 = 0, ParmString p3 = 0,
        ParmString p4 = 0)
    {
        return set(inf, p1, p2, p3, p4);
    }

    PosibErrBase& with_file(ParmString fn, int lineno = 0);

    PosibErrBase& set(const ErrorInfo*, ParmString, ParmString, ParmString, ParmString);

private:

    Error* release();
    void del();
};

template <>
class PosibErr<void> : public PosibErrBase
{
public:
    PosibErr(const PosibErrBase& other)
        : PosibErrBase(other)
    {
    }

    PosibErr()
    {
    }
};

template <typename Ret>
class PosibErr : public PosibErrBase
{
public:
    PosibErr() {}

    PosibErr(const PosibErrBase& other)
        : PosibErrBase(other)
    {
    }

    template <typename T>
    PosibErr(const PosibErr<T>& other)
        : PosibErrBase(other), data(other.data)
    {
    }

    PosibErr(const PosibErr<void>& other)
        : PosibErrBase(other)
    {
    }

    PosibErr& operator= (const PosibErr & other) {
      data = other.data;
      PosibErrBase::destroy();
      PosibErrBase::copy(other);
      return *this;
    }
    PosibErr(const Ret & d) : data(d) {}
    operator const Ret & () const {posib_handle_err(); return data;}

    Ret data;
};

#define RET_ON_ERR_SET(command, type, var) \
    type var;do{PosibErr< type > pe(command);if(pe.has_err())return PosibErrBase(pe);var=pe.data;} while(false)
#define RET_ON_ERR(command) \
    do{PosibErrBase pe(command);if(pe.has_err())return PosibErrBase(pe);}while(false)

static inline PosibErrBase make_err(const ErrorInfo * inf, 
                                    ParmString p1 = 0, ParmString p2 = 0,
                                    ParmString p3 = 0, ParmString p4 = 0)
{
    return PosibErrBase().prim_err(inf, p1, p2, p3, p4);
}

static const PosibErr<void> no_err;

//
//
//
inline String & String::operator= (const PosibErr<const char *> & s)
{
    *this = s.data;
    return *this;
}

inline bool operator== (const PosibErr<String> & x, const char * y)
{
    return x.data == y;
}
inline bool operator!= (const PosibErr<String> & x, const char * y)
{
    return x.data != y;
}

inline ParmString::ParmString(const PosibErr<const char *> & s)
: _str(s.data), _size(UINT_MAX) {}

inline ParmString::ParmString(const PosibErr<String> & s)
: _str(s.data.c_str()), _size(s.data.size()) {}

#endif  // AC_COMMON_POSIB_ERR_HPP_
